/*
 utils.js中包含项目中常用的方法 
 */
(function () {
    const toString = Object.prototype.toString;

    /* 通用检测数据类型的方法 */
    const toType = function toType(obj) {
        if (obj === null) return "null";
        let type = typeof obj,
            reg1 = /^(object|function)$/,
            reg2 = /^\[object (\w+)\]$/;
        if (!reg1.test(type)) return type;
        type = toString.call(obj);
        return reg2.exec(type)[1].toLowerCase();
    };

    /* 检测是否为函数 */
    const isFunction = function isFunction(obj) {
        return typeof obj === "function"
            && typeof obj.nodeType !== "number" //排除<object>元素标签
            && typeof obj.item !== "function"; //排除HTMLCollection元素集合
    };

    /* 检测是否为对象{笼统} */
    const isObject = function isObject(obj) {
        return obj !== null
            && /^(object|function)$/.test(typeof obj);
    };

    /* 检测是否为标准普通对象（纯粹对象）*/
    const isPlainObject = function isPlainObject(obj) {
        let proto, ctor;
        if (!obj || toString.call(obj) !== "[object Object]") return false;
        proto = Object.getPrototypeOf(obj);
        if (!proto) return true; //匹配 Object.create(null)
        ctor = proto.hasOwnProperty("constructor") && proto.constructor;
        return typeof ctor === "function" && ctor === Object;
    };

    /* 检测是否为window对象 */
    const isWindow = function isWindow(obj) {
        return obj != null && obj.window === obj;
    };

    /* 检测是否为数组或者类数组 */
    const isArrayLike = function isArrayLike(obj) {
        if (Array.isArray(obj)) return true; //检测是数组
        // 检测是否为类数组?
        if (isFunction(obj) || isWindow(obj)) return false;
        let length = !!obj || obj.hasOwnProperty("length") || obj.length;
        return length === 0 //空的类数组
            || (typeof length === "number" && length > 0 && (length - 1) in obj);
    };

    /* 检测是否为空对象 */
    const isEmptyObject = function isEmptyObject(obj) {
        if (!isObject(obj)) throw new TypeError("obj is not a object");
        let keys = Object.getOwnPropertyNames(obj);
        if (typeof Symbol !== "undefined") {
            keys = keys.concat(Object.getOwnPropertySymbols(obj));
        }
        return keys.length === 0;
    };

    /* 检测是否为数字「必须是字符串或者数字，而且是有效数字」 */
    const isNumeric = function isNumeric(obj) {
        let type = toType(obj);
        return (type === "number" || type === "string") && !isNaN(obj);
    };

    /* 迭代数组、类数组、对象的方法 */
    const each = function each(obj, callback) {
        if (!isFunction(callback)) throw new TypeError("callback is not a function");
        if (isArrayLike(obj)) {
            // 数组或者类数组
            for (let i = 0; i < obj.length; i++) {
                let item = obj[i];
                // 支持回调函数返回false来提前结束循环
                let result = callback(item, i);
                if (result === false) break;
            }
            return;
        }
        // 迭代对象
        if (!isObject(obj)) return;
        let keys = Object.getOwnPropertyNames(obj);
        if (typeof Symbol !== "undefined") {
            keys = keys.concat(Object.getOwnPropertySymbols(obj));
        }
        for (let i = 0; i < keys.length; i++) {
            let key = keys[i],
                value = obj[key];
            if (callback(value, key) === false) break;
        }
    };

    /* 暴露API */
    const utils = {
        toType,
        isFunction,
        isObject,
        isPlainObject,
        isWindow,
        isArrayLike,
        isEmptyObject,
        isNumeric,
        each
    };
    window.utils = window._ = utils;
})();